import time
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, VotingClassifier, BaggingClassifier, AdaBoostClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from xgboost import XGBClassifier
from sklearn.ensemble import StackingClassifier
from tabulate import tabulate
import logging
import numpy as np
import gradio as gr
import cv2
import faiss
from util import createXY
import joblib


# 配置logging, 确保能够打印正在运行的函数名
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 数据加载和预处理
X, y = createXY(train_folder="data/train", dest_folder=".",method='flat')
X = np.array(X).astype('float32')
faiss.normalize_L2(X)  # 对数据进行L2归一化
y = np.array(y)
logging.info("数据加载和预处理完成。")

# 数据集分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=2023)
logging.info("数据集划分为训练集和测试集。")

# 创建逻辑回归分类器
logistic_clf = LogisticRegression(max_iter=1000)

 # 创建随机森林分类器
random_forest_clf = RandomForestClassifier(random_state=42)

# 创建支持向量机分类器
# probability=True表示需要计算概率值，这样才能使用软投票
svm_clf = SVC(probability=True)

# 创建硬投票分类器，包含逻辑回归、随机森林和支持向量机三个分类器
voting_clf_hard = VotingClassifier(
    estimators=[('lr', logistic_clf), ('rf', random_forest_clf), ('svc', svm_clf)],
    voting='hard' # 采用硬投票, 即选择票数最多的类别作为预测结果
)

 # 创建软投票分类器，包含逻辑回归、随机森林和支持向量机三个分类器
voting_clf_soft = VotingClassifier(
    estimators=[('lr', logistic_clf), ('rf', random_forest_clf), ('svc', svm_clf)],
    voting='soft' # 采用软投票, 即选择概率之和最大的类别作为预测结果
)

# 创建Bagging分类器
bag_clf = BaggingClassifier(
    DecisionTreeClassifier(), # 基分类器选用决策树分类器
    n_estimators=500,         # 500个分类器
    max_samples=100,          # 每个分类器的训练集包含100个样本
    bootstrap=True,           # 有放回的采样
    n_jobs=-1,                # 使用所有CPU核
)

# 创建Pasting分类器
paste_clf = BaggingClassifier(
    DecisionTreeClassifier(), # 基分类器选用决策树分类器
    n_estimators=500,         # 500个分类器
    max_samples=100,          # 每个分类器的训练集包含100个样本
    bootstrap=False,          # 无放回的采样
    n_jobs=-1,                # 使用所有CPU核
)

# 创建AdaBoost分类器
ada_clf = AdaBoostClassifier(
    DecisionTreeClassifier(max_depth=1),  # 基分类器选用决策树分类器
    n_estimators=200,                     # 200个分类器
    algorithm="SAMME.R",                  # 使用SAMME.R算法
    learning_rate=0.5                     # 学习率为0.5, 即每个分类器的权重缩减系数为0.5
) 

 # 创建一个梯度提升分类器
xgb_clf = XGBClassifier(
     n_estimators=200,   # 200个分类器
     max_depth=2,        # 每个分类器的最大深度为2
     learning_rate=0.5   # 学习率为0.5, 即每个分类器的权重缩减系数为0.5
 )

 # 创建一个堆叠分类器
stacking_clf = StackingClassifier(
     estimators=[('lr', logistic_clf), ('rf', random_forest_clf), ('svc', svm_clf)],
    final_estimator=LogisticRegression() # 最终分类器选用逻辑回归分类器
)

clfs = {
    "logistic_regression": logistic_clf,
    "random_forest": random_forest_clf,
    "svm": svm_clf,
    "hard_voting": voting_clf_hard,
    "soft_voting": voting_clf_soft,
    "bagging": bag_clf,
    "pasting": paste_clf,
    "adaboost": ada_clf,
    "gradient_boosting": xgb_clf,
    "stacking": stacking_clf
}

results = []

# 训练和评估分类器
for name, clf in clfs.items():# 遍历字典中的每一个键值对:(模型名称, 模型对象)
    start_time = time.time()
    clf.fit(X_train, y_train)
    train_time = time.time() - start_time
    logging.info(f"{name}模型训练完成，用时：{train_time:.4f}秒。")

    start_time = time.time()
    accuracy = clf.score(X_test, y_test)
    pred_time = time.time() - start_time
    logging.info(f"{name}模型评估完成，用时:{pred_time:.4f}秒。")

    results.append([name, train_time, pred_time, accuracy])

# 打印结果表格
headers = ["Classifier", "Training Time (s)", "Prediction Time (s)", "Accuracy"]
print(tabulate(results, headers=headers, tablefmt="simple"))

# Rest of the code...
# 训练并评估模型，记录最佳准确率及其对应的模型
best_accuracy = 0
best_model = None
for name,clf in clfs.items():
    if accuracy > best_accuracy:
        best_accuracy = accuracy
        best_model = clf
    logging.info(f"{clf.__class__.__name__} 准确率： {accuracy:.4f}")

logging.info(f"最佳准确率： {best_accuracy:.4f}")
logging.info(f"最佳模型： {best_model.__class__.__name__}")

# 保存准确率最高的模型
joblib.dump(best_model, 'best_model.pkl')
#加载模型
best_model=joblib.load('best_model.pkl')
# Gradio界面函数
# def classify_image(image):
#     img = cv2.imdecode(np.fromstring(image.read(), np.uint8), cv2.IMREAD_COLOR)
#     img = cv2.resize(img, (224, 224))
#     img = img.reshape(1, -1).astype('float32')
#     faiss.normalize_L2(img)

#     prediction = best_model.predict(img)
#     return "狗" if prediction[0] == 0 else "猫"
def classify_image(image):
    img = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)   #将图像从BGR格式转换为RGB格式
    img = cv2.resize(img, (32, 32))   #将图像调整为32x32
    img = img.reshape(1, -1)   #将图像展平
    prediction = best_model.predict(img)   #预测图像的标签
    return '猫' if prediction[0]==1 else '狗'   #返回预测结果

# 创建Gradio界面
iface = gr.Interface(fn=classify_image, inputs="image", outputs="text")
iface.launch()